/* *
 * --ライセンスについて--
 *
 * 「本ファイルの内容は Mozilla Public License Version 1.1 (「本ライセンス」)
 * の適用を受けます。
 * 本ライセンスに従わない限り本ファイルを使用することはできません。
 * 本ライセンスのコピーは http://www.mozilla.org/MPL/ から入手できます。
 *
 * 本ライセンスに基づき配布されるソフトウェアは、「現状のまま」で配布されるものであり、
 * 明示的か黙示的かを問わず、いかなる種類の保証も行われません。
 * 本ライセンス上の権利および制限を定める具体的な文言は、本ライセンスを参照してください。
 *
 * オリジナルコードおよび初期開発者は、N_H (h.10x64@gmail.com) です。
 *
 * N_H によって作成された部分の著作権表示は次のとおりです。
 *
 * Copyright (C)N_H 2012
 *
 * このファイルの内容は、上記に代えて、
 * GNU General License version2 以降 (以下 GPL とする)、
 * GNU Lesser General Public License Version 2.1 以降 (以下 LGPL とする)、
 * の条件に従って使用することも可能です。
 * この場合、このファイルの使用には上記の条項ではなく GPL または LGPL の条項が適用されます。
 * このファイルの他者による使用を GPL または LGPL の条件によってのみ許可し、
 * MPL による使用を許可したくない対象者は、上記の条項を削除することでその意思を示し、
 * 上記条項を GPL または LGPL で義務付けられている告知およびその他の条項に置き換えてください。
 * 対象者が上記の条項を削除しない場合、
 * 受領者は MPL または GPL または LGPL ライセンスのいずれによってもこのファイルを
 * 使用することができます。」
 *
 * -- License --
 *
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License。You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND、either express or implied。See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Initial Developer of the Original Code is
 *   N_H (h.10x64@gmail.com).
 *
 * Portions created by the Initial Developer are Copyright (C)N_H 2012
 * the Initial Developer。All Rights Reserved.
 *
 * Alternatively、the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL")、or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above。If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL、and not to allow others to
 * use your version of this file under the terms of the MPL、indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL。If you do not delete
 * the provisions above、a recipient may use your version of this file under
 * the terms of any one of the MPL、the GPL or the LGPL.
 *
 * */
package com.magiciansforest.audio.soundrenderer.logic;

import com.jme3.audio.Listener;
import com.jme3.material.Material;
import com.jme3.material.RenderState.BlendMode;
import com.jme3.math.ColorRGBA;
import com.jme3.renderer.queue.RenderQueue.Bucket;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Sphere;
import com.magiciansforest.audio.soundrenderer.ApplicationBase;
import com.magiciansforest.audio.soundrenderer.event.MovieChangeEvent;
import com.magiciansforest.audio.soundrenderer.event.SoundSourceChangeEvent;
import com.magiciansforest.audio.soundrenderer.event.SoundSourceChangeEventListener;
import com.magiciansforest.audio.soundrenderer.logic.sound.AudioRenderThread;
import java.io.IOException;
import java.util.*;

/**
 *
 * @author N_H
 */
public class SoundEnvironment  {

    protected ApplicationBase app;
    protected Movie mov;
    protected Listener observer;
    protected Map<Long, MMDSoundSource> sources = new HashMap<Long, MMDSoundSource>();
    protected AudioRenderThread audioRenderer;
    private List<SoundSourceChangeEventListener> listeners = new ArrayList<SoundSourceChangeEventListener>();

    public SoundEnvironment(ApplicationBase app, Movie mov) {
        this.mov = mov;
        this.app = app;
        this.observer = app.getListener();
        this.audioRenderer = app.getBinauralAudioRenderer();
    }

    public void reset() {
        for (MMDSoundSource src : sources.values()) {
            app.addOperation(new DetachChild(src.getSource().getParent(), src.getSource()));
        }

        long[] ids = new long[sources.size()];
        int counter = 0;
        for (Long id : sources.keySet()) {
            ids[counter] = id;

            audioRenderer.removeSoundSource(sources.get(id).getSource());

            counter++;
        }

        sources.clear();

        for (int i = 0; i < ids.length; i++) {
            fireSoundSourceChangeEvent(new SoundSourceChangeEvent(this, ids[i], MovieChangeEvent.REMOVED));
        }
    }

    public long addSoundSource() {
        MMDSoundSource src = new MMDSoundSource(IDCreator.getSoundSourceIDCreator().getNewID(), mov, audioRenderer, -1, null);

        //Attach geometry
        Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
        mat.setColor("Color", new ColorRGBA(0f, 0f, 1f, 0.75f));
        mat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
        Sphere s = new Sphere(16, 16, 1);
        Geometry g = new Geometry(src.getName() + "_geom", s);
        g.setMaterial(mat);
        g.setQueueBucket(Bucket.Transparent);
        app.addOperation(new AttachChild(src.getSource(), g));
        app.addOperation(new AttachChild(app.getRootNode(), src.getSource()));

        sources.put(src.getID(), src);
        audioRenderer.addSoundSource(src.getSource());

        fireSoundSourceChangeEvent(new SoundSourceChangeEvent(this, src.getID(), SoundSourceChangeEvent.CREATED));

        return src.getID();
    }

    public void removeSoundSource(MMDSoundSource src) {
        if (!sources.containsValue(src)) {
            return;
        }

        app.addOperation(new DetachAllChildren(src.getSource()));
        app.addOperation(new DetachChild(src.getSource().getParent(), src.getSource()));

        sources.remove(src.getID());
        audioRenderer.removeSoundSource(src.getSource());

        fireSoundSourceChangeEvent(new SoundSourceChangeEvent(this, src.getID(), SoundSourceChangeEvent.REMOVED));
    }

    public MMDSoundSource getSource(long id) {
        return sources.get(id);
    }

    public Iterator<MMDSoundSource> getSourceIterator() {
        return sources.values().iterator();
    }

    public boolean containsSource(long id) {
        return sources.containsKey(id);
    }

    public boolean setWavFile(long id, String filePath) throws IOException {
        if (!sources.containsKey(id)) {
            return false;
        }

        MMDSoundSource src = sources.get(id);
        src.setWavFile(filePath);

        fireSoundSourceChangeEvent(new SoundSourceChangeEvent(this, src.getID(), SoundSourceChangeEvent.CHANGED));

        return true;
    }

    public boolean setName(long id, String name) {
        if (!sources.containsKey(id)) {
            return false;
        }

        MMDSoundSource src = sources.get(id);
        src.setName(name);

        fireSoundSourceChangeEvent(new SoundSourceChangeEvent(this, src.getID(), SoundSourceChangeEvent.CHANGED));

        return true;
    }

    public boolean setParentID(long id, long characterID) {
        if (!sources.containsKey(id)) {
            return false;
        }

        MMDSoundSource src = sources.get(id);
        src.setParentID(characterID);

        fireSoundSourceChangeEvent(new SoundSourceChangeEvent(this, src.getID(), SoundSourceChangeEvent.CHANGED));

        return true;
    }

    public boolean setBoneName(long id, String boneName) {
        if (!sources.containsKey(id)) {
            return false;
        }

        MMDSoundSource src = sources.get(id);
        src.setBoneName(boneName);

        fireSoundSourceChangeEvent(new SoundSourceChangeEvent(this, src.getID(), SoundSourceChangeEvent.CHANGED));

        return true;
    }

    public boolean setStartTime(long id, int movFrameNo) {
        if (!sources.containsKey(id)) {
            return false;
        }

        MMDSoundSource src = sources.get(id);
        if (src.setStartFrame(movFrameNo)) {
            fireSoundSourceChangeEvent(new SoundSourceChangeEvent(this, src.getID(), SoundSourceChangeEvent.CHANGED));
        }

        return true;
    }

    public boolean setEndTime(long id, int movFrameNo) {
        if (!sources.containsKey(id)) {
            return false;
        }

        MMDSoundSource src = sources.get(id);
        if (src.setEndFrame(movFrameNo)) {
            fireSoundSourceChangeEvent(new SoundSourceChangeEvent(this, src.getID(), SoundSourceChangeEvent.CHANGED));
        }

        return true;
    }

    public boolean setVolume(long id, double volume) {
        if (!sources.containsKey(id)) {
            return false;
        }

        sources.get(id).setVolume(volume);
        return true;
    }

    public void setTranslation(long id, float x, float y, float z) {
        if (!sources.containsKey(id)) {
            return;
        }

        app.addOperation(new SetTranslation(sources.get(id).getSource(), x, y, z));
    }
    
    public void setCelsius(double c) {
        audioRenderer.getAir().setCelsius(c);
    }
    
    public double getCelsius() {
        return audioRenderer.getAir().getTemperature() - 273.15;
    }
    
    public void setAtmosphericPressure(double atm) {
        audioRenderer.getAir().setAtmosphericPressure(atm);
    }
    
    public double getAtmosphericPressure() {
        return audioRenderer.getAir().getAtmosphericPressure();
    }
    
    public void setHumidity(double molPercent) {
        audioRenderer.getAir().setHumidity(molPercent);
    }
    
    public double getHumidity() {
        return audioRenderer.getAir().getHumidity();
    }

    public void addSoundSourceChangeEventListener(SoundSourceChangeEventListener listener) {
        if (!listeners.contains(listener)) {
            listeners.add(listener);
        }
    }

    public void removeSoundSourceChangeEventListener(SoundSourceChangeEventListener listener) {
        if (listeners.contains(listener)) {
            listeners.remove(listener);
        }
    }

    private void fireSoundSourceChangeEvent(SoundSourceChangeEvent e) {
        for (SoundSourceChangeEventListener listener : listeners) {
            listener.SoundSourceChange(e);
        }
    }
}
